<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>useState</title>
    <script src="../js/react.production.min.js"></script>
    <script src="../js/react-dom.production.min.js"></script>
    <script src="../js/babel.min.js"></script>
  </head>
  <body>
    <div id="root"></div>
  </body>
  <script type="text/babel">
    const root = ReactDOM.createRoot(document.getElementById("root"));

    // 声明存储各组件状态及修改状态函数的数组
    const states = [];
    const stateSetters = [];

    // 声明控制存储组件状态的索引值
    let index = 0;

    // 创建存储或更新组件状态的函数
    function createState(initialState, index) {
      console.log(states);
      let state = states[index] !== undefined ? states[index] : initialState;
      return state;
    }

    // 创建修改组件状态函数的函数
    function createStateSetter(index) {
      // 返回实际修改组件状态的函数，进行封装目的是使用闭包的方式与指定的索引值进行绑定，在使用时无需感知具体的索引
      return function (newState) {
        // 分别定义新值是普通值还是函数时的用法
        if (typeof newState === "function") {
          states[index] = newState(states[index]);
        } else {
          states[index] = newState;
        }
        // 更新完状态，重新渲染组件
        render(App);
      };
    }

    // 声明 hook 函数
    function useState(initialState) {
      // 创建状态及创建状态修改函数
      const state = createState(initialState, index);
      const setState = createStateSetter(index);

      // 根据对应的索引存储到数组中
      states[index] = state;
      stateSetters[index] = setState;

      // 每调用一次 useState 函数，意味着创建了新的状态，需要将索引值 加一
      index++;

      // 向组件返回已经存储好的状态及对应的修改函数
      return [state, setState];
    }

    // 组件渲染函数
    // 每次组件重新渲染时，useState 都会重新执行
    // 只有组件第一次执行时是创建新状态，后续都是获取已有状态
    // 需要将索引值归零
    let render = () => {
      index = 0;
      root.render(App());
    };

    let App = () => {
      const [count, setCount] = useState(0);
      const add = () => {
        setCount(count + 1);
      };

      const [msg, setMsg] = useState("");
      const input = (e) => {
        setMsg(e.target.value);
      };

      return (
        <div className="App">
          <p>{count}</p>
          <button onClick={add}>增加</button>
          <hr />
          <input type="text" value={msg} onChange={input} />
        </div>
      );
    };

    // 初始渲染
    render(App);
  </script>
</html>
